package com.bsi.core.authorization;

import com.bsi.core.converter.DateConverterEx;
import com.bsi.core.exception.BusinessException;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Iterator;

/*
 * Author: liusongs
 * Date: 2019/8/30 10:32
 * MSG: 此注解只有在controller的方法参数中使用才能生效(@TokenValue注解除String不需要写name值外，其余都需要写name值,name值代表所需要将token封装到实体类哪个属性里)
 * Content:  配合@TokenValue,将tokenvalue注解里面的name值来解析到对应属性里
 * mode:  一、@TokenValue String obj   二、 @TokenValue Customer customer   三、@TokenValue Pojo p 其中pojo里必要customerId字段
 */
public class TokenHandlerArgumentMethodResovler implements HandlerMethodArgumentResolver {

    private static String TOKEN_USER_ID;

    @Autowired
    private DateConverterEx dateConverterEx;

    public void setTokenUserId(String tokenUserId) {
        TOKEN_USER_ID = tokenUserId;
    }

    /**
     * 触发条件，判断是否参数上是否有tokenValue注解
     *
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(TokenValue.class);
    }

    /**
     * 进行解析
     *
     * @param methodParameter
     * @param modelAndViewContainer
     * @param nativeWebRequest
     * @param webDataBinderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        Object o = doResovler(methodParameter, nativeWebRequest);
        return o;
    }

    /**
     * 将前端的传过来的参数+ token内部包含的userId封装到controller的参数中
     *
     * @param methodParameter  拦截到的方法中的参数
     * @param nativeWebRequest request
     * @return 生成的对象
     * @throws BusinessException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public Object doResovler(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) throws BusinessException, IllegalAccessException, InstantiationException {
        HttpServletRequest servletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        //判断请求头中是否有userId这个值
        String header = servletRequest.getHeader(TOKEN_USER_ID); //获取到当前的userId
        if (header == null) {
            throw new BusinessException("[TokenHandlerArgumentMethodResovler.resolveArgument]解析失败，当前请求头中未包含" + TOKEN_USER_ID + "的值");
        }
        //封装参数
        Class<?> parameterType = methodParameter.getParameterType();
        if (parameterType.isPrimitive()) {// 如果是基本数据类型则直接赋值返回
            throw new BusinessException("[TokenHandlerArgumentMethodResovler.resolveArgument]解析失败，基本数据类型无法解析塞入token值");
        }
        Object o = parameterType.newInstance();
        // 字符串接收则直接返回
        if (o instanceof String) {
            return header;
        }
        // 对象接收
        BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(o);
        // 封装前端带过来属性
        for (Iterator<String> parameterNames = nativeWebRequest.getParameterNames(); parameterNames.hasNext(); ) {
            String fieldName = parameterNames.next(); // 属性名
            // 判断实体类中是否有这个属性
            String parameter = nativeWebRequest.getParameter(fieldName);// 属性值
            Class<?> propertyType = beanWrapper.getPropertyType(fieldName); //判断是不是日期类型
            if (propertyType == null) { // 该属性实体类中没有，则跳过
                continue;
            }
            if (propertyType != null && (propertyType.getName().endsWith(Date.class.getName()) || propertyType.getName().equals(java.sql.Date.class.getName()))) {
                Date convert = dateConverterEx.convert(parameter);
                beanWrapper.setPropertyValue(fieldName, convert);
                continue;
            }
            beanWrapper.setPropertyValue(fieldName, parameter);
        }
        // 封装token值
        String name = methodParameter.getParameterAnnotation(TokenValue.class).name();
        if (!StringUtils.isEmpty(name)) {
            beanWrapper.setPropertyValue(name, header);
        }
        return o;
    }
}
